package me.leon.ext.android

import android.annotation.TargetApi
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.view.MotionEvent
import android.view.WindowManager
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import java.util.*
import me.leon.ext.Utils.app
import me.leon.ext.Utils.runOnUiThreadDelayed
import me.leon.ext.android.PermissionUtils.OnRationaleListener.ShouldRequest
import me.leon.ext.constant.PermissionConstants

/**
 * <pre> author: Blankj blog : http://blankj.com time : 2017/12/29 desc : utils about permission
 * </pre> *
 */
class PermissionUtils private constructor(vararg permissions: String) {
    private var mOnRationaleListener: OnRationaleListener? = null
    private var mSimpleCallback: SimpleCallback? = null
    private var mFullCallback: FullCallback? = null
    private var mThemeCallback: ThemeCallback? = null
    private val mPermissions: MutableSet<String>
    private var mPermissionsRequest: MutableList<String> = mutableListOf()
    private var mPermissionsGranted: MutableList<String> = mutableListOf()
    private var mPermissionsDenied: MutableList<String> = mutableListOf()
    private var mPermissionsDeniedForever: MutableList<String> = mutableListOf()

    /**
     * Set rationale listener.
     *
     * @param listener The rationale listener.
     * @return the single [PermissionUtils] instance
     */
    fun rationale(listener: OnRationaleListener?): PermissionUtils {
        mOnRationaleListener = listener
        return this
    }

    /**
     * Set the simple call back.
     *
     * @param callback the simple call back
     * @return the single [PermissionUtils] instance
     */
    fun callback(callback: SimpleCallback?): PermissionUtils {
        mSimpleCallback = callback
        return this
    }

    /**
     * Set the full call back.
     *
     * @param callback the full call back
     * @return the single [PermissionUtils] instance
     */
    fun callback(callback: FullCallback?): PermissionUtils {
        mFullCallback = callback
        return this
    }

    /**
     * Set the theme callback.
     *
     * @param callback The theme callback.
     * @return the single [PermissionUtils] instance
     */
    fun theme(callback: ThemeCallback?): PermissionUtils {
        mThemeCallback = callback
        return this
    }

    /** Start request. */
    fun request() {
        mPermissionsGranted = ArrayList()
        mPermissionsRequest = ArrayList()
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            mPermissionsGranted.addAll(mPermissions)
            requestCallback()
        } else {
            for (permission in mPermissions) {
                if (isGranted(permission)) {
                    mPermissionsGranted.add(permission)
                } else {
                    mPermissionsRequest.add(permission)
                }
            }
            if (mPermissionsRequest.isEmpty()) {
                requestCallback()
            } else {
                startPermissionActivity()
            }
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    private fun startPermissionActivity() {
        mPermissionsDenied = ArrayList()
        mPermissionsDeniedForever = ArrayList()
        PermissionActivity.start(app, PermissionActivity.TYPE_RUNTIME)
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    private fun rationale(activity: Activity): Boolean {
        var isRationale = false
        if (mOnRationaleListener != null) {
            for (permission in mPermissionsRequest!!) {
                if (activity.shouldShowRequestPermissionRationale(permission)) {
                    getPermissionsStatus(activity)
                    mOnRationaleListener!!.rationale(
                        object : ShouldRequest {
                            override fun again(again: Boolean) {
                                activity.finish()
                                if (again) {
                                    startPermissionActivity()
                                } else {
                                    requestCallback()
                                }
                            }
                        }
                    )
                    isRationale = true
                    break
                }
            }
            mOnRationaleListener = null
        }
        return isRationale
    }

    @RequiresApi(Build.VERSION_CODES.M)
    private fun getPermissionsStatus(activity: Activity) {
        for (permission in mPermissionsRequest) {
            if (isGranted(permission)) {
                mPermissionsGranted.add(permission)
            } else {
                mPermissionsDenied.add(permission)
                if (!activity.shouldShowRequestPermissionRationale(permission)) {
                    mPermissionsDeniedForever.add(permission)
                }
            }
        }
    }

    private fun requestCallback() {
        if (mSimpleCallback != null) {
            if (mPermissionsRequest.isEmpty() || mPermissions.size == mPermissionsGranted.size) {
                mSimpleCallback!!.onGranted()
            } else {
                if (mPermissionsDenied.isNotEmpty()) {
                    mSimpleCallback!!.onDenied()
                }
            }
            mSimpleCallback = null
        }
        if (mFullCallback != null) {
            if (mPermissionsRequest.isEmpty() || mPermissions.size == mPermissionsGranted.size) {
                mFullCallback!!.onGranted(mPermissionsGranted)
            } else {
                if (mPermissionsDenied.isNotEmpty()) {
                    mFullCallback!!.onDenied(mPermissionsDeniedForever, mPermissionsDenied)
                }
            }
            mFullCallback = null
        }
        mOnRationaleListener = null
        mThemeCallback = null
    }

    @RequiresApi(Build.VERSION_CODES.M)
    private fun onRequestPermissionsResult(activity: Activity) {
        getPermissionsStatus(activity)
        requestCallback()
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    class PermissionActivity : Activity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            window.addFlags(
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
            )
            val byteExtra = intent.getIntExtra(TYPE, TYPE_RUNTIME)
            if (byteExtra == TYPE_RUNTIME) {
                if (sInstance == null) {
                    super.onCreate(savedInstanceState)
                    Log.e("PermissionUtils", "request permissions failed")
                    finish()
                    return
                }
                if (sInstance!!.mThemeCallback != null)
                    sInstance!!.mThemeCallback!!.onActivityCreate(this)
                super.onCreate(savedInstanceState)
                if (sInstance!!.rationale(this)) {
                    return
                }
                if (sInstance!!.mPermissionsRequest.isNotEmpty()) {
                    val size = sInstance!!.mPermissionsRequest.size
                    if (size <= 0) {
                        finish()
                        return
                    }
                    requestPermissions(sInstance!!.mPermissionsRequest.toTypedArray(), 1)
                }
            } else if (byteExtra == TYPE_WRITE_SETTINGS) {
                super.onCreate(savedInstanceState)
                startWriteSettingsActivity(this, TYPE_WRITE_SETTINGS)
            } else if (byteExtra == TYPE_DRAW_OVERLAYS) {
                super.onCreate(savedInstanceState)
                startOverlayPermissionActivity(this, TYPE_DRAW_OVERLAYS)
            }
        }

        override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array<String>,
            grantResults: IntArray
        ) {
            if (sInstance != null && sInstance!!.mPermissionsRequest.isNotEmpty()) {
                sInstance!!.onRequestPermissionsResult(this)
            }
            finish()
        }

        override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
            finish()
            return true
        }

        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
            if (requestCode == TYPE_WRITE_SETTINGS) {
                if (sSimpleCallback4WriteSettings == null) return
                if (isGrantedWriteSettings) {
                    sSimpleCallback4WriteSettings!!.onGranted()
                } else {
                    sSimpleCallback4WriteSettings!!.onDenied()
                }
                sSimpleCallback4WriteSettings = null
            } else if (requestCode == TYPE_DRAW_OVERLAYS) {
                if (sSimpleCallback4DrawOverlays == null) return
                runOnUiThreadDelayed(
                    Runnable {
                        if (isGrantedDrawOverlays) {
                            sSimpleCallback4DrawOverlays!!.onGranted()
                        } else {
                            sSimpleCallback4DrawOverlays!!.onDenied()
                        }
                        sSimpleCallback4DrawOverlays = null
                    },
                    100
                )
            }
            finish()
        }

        companion object {
            private const val TYPE = "TYPE"
            const val TYPE_RUNTIME = 0x01
            const val TYPE_WRITE_SETTINGS = 0x02
            const val TYPE_DRAW_OVERLAYS = 0x03
            fun start(context: Context, type: Int) {
                val starter = Intent(context, PermissionActivity::class.java)
                starter.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                starter.putExtra(TYPE, type)
                context.startActivity(starter)
            }
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // interface
    ///////////////////////////////////////////////////////////////////////////
    interface OnRationaleListener {
        fun rationale(shouldRequest: ShouldRequest?)
        interface ShouldRequest {
            fun again(again: Boolean)
        }
    }

    interface SimpleCallback {
        fun onGranted()
        fun onDenied()
    }

    interface FullCallback {
        fun onGranted(permissionsGranted: List<String>?)
        fun onDenied(permissionsDeniedForever: List<String>?, permissionsDenied: List<String>?)
    }

    interface ThemeCallback {
        fun onActivityCreate(activity: Activity?)
    }

    companion object {
        private val PERMISSIONS = permissions
        private var sInstance: PermissionUtils? = null
        private var sSimpleCallback4WriteSettings: SimpleCallback? = null
        private var sSimpleCallback4DrawOverlays: SimpleCallback? = null

        /**
         * Return the permissions used in application.
         *
         * @return the permissions used in application
         */
        val permissions: List<String>
            get() = getPermissions(app.packageName)

        /**
         * Return the permissions used in application.
         *
         * @param packageName The name of the package.
         * @return the permissions used in application
         */
        fun getPermissions(packageName: String): List<String> {

            return try {
                val permissions =
                    app.packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)
                        .requestedPermissions
                        ?: return emptyList()
                listOf(*permissions)
            } catch (e: PackageManager.NameNotFoundException) {
                e.printStackTrace()
                emptyList()
            }
        }

        /**
         * Return whether *you* have been granted the permissions.
         *
         * @param permissions The permissions.
         * @return `true`: yes<br></br>`false`: no
         */
        fun isGranted(vararg permissions: String): Boolean {
            for (permission in permissions) {
                if (!isGranted(permission)) {
                    return false
                }
            }
            return true
        }

        private fun isGranted(permission: String): Boolean {
            return (Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
                PackageManager.PERMISSION_GRANTED ==
                    ContextCompat.checkSelfPermission(app, permission))
        }

        /**
         * Return whether the app can modify system settings.
         *
         * @return `true`: yes<br></br>`false`: no
         */
        @get:RequiresApi(api = Build.VERSION_CODES.M)
        val isGrantedWriteSettings: Boolean
            get() = Settings.System.canWrite(app)

        @RequiresApi(api = Build.VERSION_CODES.M)
        fun requestWriteSettings(callback: SimpleCallback?) {
            if (isGrantedWriteSettings) {
                callback?.onGranted()
                return
            }
            sSimpleCallback4WriteSettings = callback
            PermissionActivity.start(app, PermissionActivity.TYPE_WRITE_SETTINGS)
        }

        @TargetApi(Build.VERSION_CODES.M)
        private fun startWriteSettingsActivity(activity: Activity, requestCode: Int) {
            val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
            intent.data = Uri.parse("package:" + app.packageName)
            if (!isIntentAvailable(intent)) {
                launchAppDetailsSettings()
                return
            }
            activity.startActivityForResult(intent, requestCode)
        }

        /**
         * Return whether the app can draw on top of other apps.
         *
         * @return `true`: yes<br></br>`false`: no
         */
        @get:RequiresApi(api = Build.VERSION_CODES.M)
        val isGrantedDrawOverlays: Boolean
            get() = Settings.canDrawOverlays(app)

        @RequiresApi(api = Build.VERSION_CODES.M)
        fun requestDrawOverlays(callback: SimpleCallback?) {
            if (isGrantedDrawOverlays) {
                callback?.onGranted()
                return
            }
            sSimpleCallback4DrawOverlays = callback
            PermissionActivity.start(app, PermissionActivity.TYPE_DRAW_OVERLAYS)
        }

        @TargetApi(Build.VERSION_CODES.M)
        private fun startOverlayPermissionActivity(activity: Activity, requestCode: Int) {
            val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
            intent.data = Uri.parse("package:" + app.packageName)
            if (!isIntentAvailable(intent)) {
                launchAppDetailsSettings()
                return
            }
            activity.startActivityForResult(intent, requestCode)
        }

        /** Launch the application's details settings. */
        fun launchAppDetailsSettings() {
            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
            intent.data = Uri.parse("package:" + app.packageName)
            if (!isIntentAvailable(intent)) return
            app.startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
        }

        /**
         * Set the permissions.
         *
         * @param permissions The permissions.
         * @return the single [PermissionUtils] instance
         */
        fun permission(
            @PermissionConstants.Permission vararg permissions: String
        ): PermissionUtils {
            return PermissionUtils(*permissions)
        }

        private fun isIntentAvailable(intent: Intent): Boolean {
            return app.packageManager
                .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
                .isNotEmpty()
        }
    }

    init {
        mPermissions = LinkedHashSet()
        for (permission in permissions) {
            for (aPermission in PermissionConstants.getPermissions(permission)) {
                if (PERMISSIONS.contains(aPermission)) {
                    mPermissions.add(aPermission)
                }
            }
        }
        sInstance = this
    }
}
